package com.gitee.wsl.doc.sample.opml

import com.gitee.wsl.collections.stack.stackOf
import com.gitee.wsl.doc.sample.opml.ValidityCheck.isTextBlank
import com.gitee.wsl.doc.sample.opml.ValidityCheck.requireNoText
import com.gitee.wsl.doc.sample.opml.bean.Head
import com.gitee.wsl.doc.xml.XmlPullParser
import com.gitee.wsl.text.format.format


/**
 * [com.gitee.wsl.doc.sample.opml.OpmlSectionHandler] that deals with the `head` tag
 */
internal class OpmlHeadHandler :
    com.gitee.wsl.doc.sample.opml.OpmlSectionHandler<com.gitee.wsl.doc.sample.opml.bean.Head?> {
    private val stack = stackOf<String>()
    private val appeared: MutableSet<String> = HashSet()

    private var started = false

    private var title: String? = null
    private var dateCreated: String? = null
    private var dateModified: String? = null
    private var ownerName: String? = null
    private var ownerEmail: String? = null
    private var ownerId: String? = null
    private var docs: String? = null
    private val expansionState: MutableList<Int> = ArrayList()
    private var vertScrollState: Int? = null
    private var windowTop: Int? = null
    private var windowLeft: Int? = null
    private var windowBottom: Int? = null
    private var windowRight: Int? = null

    @Throws(_root_ide_package_.com.gitee.wsl.doc.sample.opml.OpmlParseException::class)
    override fun startTag(xpp: XmlPullParser) {
        // no nested elements in the standard elements in head
        if (!stack.isEmpty() && !stack.peek().contains(":")) {
            throw _root_ide_package_.com.gitee.wsl.doc.sample.opml.OpmlParseException(
                String.format(
                    "head section contains nested element %s inside element %s",
                    xpp.name,
                    stack.peek()
                )
            )
        }

        when (xpp.name) {
            "title", "dateCreated", "dateModified", "ownerName", "ownerEmail", "ownerId", "docs", "expansionState", "vertScrollState", "windowBottom", "windowLeft", "windowRight", "windowTop" -> {
                // Each sub-element of <head> may appear once or not at all.
                // No sub-element of <head> may be repeated.
                // http://opml.org/spec2.opml#1629043127000
                // -- we currently take this to apply to standard elements only
                if (!appeared.add(xpp.name)) {
                    throw _root_ide_package_.com.gitee.wsl.doc.sample.opml.OpmlParseException(
                        String.format(
                            "encountered element <%s> more than once inside <head>",
                            xpp.name
                        )
                    )
                }
                // the standard <head> elements are ok
                stack.push(xpp.name)
            }

            else -> {
                // An OPML file may contain elements and attributes not described on this page,
                // only if those elements are defined in a namespace, as specified by the W3C.
                // http://opml.org/spec2.opml#1629042982000
                if (isTextBlank(xpp.namespace)) {
                    throw _root_ide_package_.com.gitee.wsl.doc.sample.opml.OpmlParseException(
                        String.format(
                            "encountered non-namespaced element <%s> instead of <head>",
                            xpp.name
                        )
                    )
                }
                stack.push(xpp.namespace + ":" + xpp.name)
            }
        }

        started = true
    }

    @Throws(_root_ide_package_.com.gitee.wsl.doc.sample.opml.OpmlParseException::class)
    override fun text(xpp: XmlPullParser) {
        if (stack.isEmpty()) {
            // we should be between <head> and first element
            requireNoText(xpp, "head", started)
            return
        }
        val text = xpp.text

        when (stack.peek()) {
            "title" -> title = text
            "dateCreated" -> dateCreated = text
            "dateModified" -> dateModified = text
            "ownerName" -> ownerName = text
            "ownerEmail" -> ownerEmail = text
            "ownerId" -> ownerId = text
            "docs" -> docs = text
            "expansionState" -> {
                val split = text.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                for (part in split) {
                    var part = part
                    part = part.trim { it <= ' ' }
                    if (part.isNotEmpty()) {
                        try {
                            expansionState.add(part.trim { it <= ' ' }.toInt())
                        } catch (e: NumberFormatException) {
                            throw _root_ide_package_.com.gitee.wsl.doc.sample.opml.OpmlParseException("expansionState must be a comma-separated list of line numbers")
                        }
                    }
                }
            }

            "vertScrollState" -> {
                val trimmed = text.trim { it <= ' ' }
                vertScrollState = if (trimmed.isNotEmpty()) {
                    try {
                        trimmed.trim { it <= ' ' }.toInt()
                    } catch (e: NumberFormatException) {
                        throw _root_ide_package_.com.gitee.wsl.doc.sample.opml.OpmlParseException("vertScrollState must be a number")
                    }
                } else {
                    null
                }
            }

            "windowBottom" -> {
                val trimmed = text.trim { it <= ' ' }
                windowBottom = if (trimmed.isNotEmpty()) {
                    try {
                        text.trim { it <= ' ' }.toInt()
                    } catch (e: NumberFormatException) {
                        throw _root_ide_package_.com.gitee.wsl.doc.sample.opml.OpmlParseException("windowBottom must be a number")
                    }
                } else {
                    null
                }
            }

            "windowLeft" -> {
                val trimmed = text.trim { it <= ' ' }
                windowLeft = if (trimmed.isNotEmpty()) {
                    try {
                        text.trim { it <= ' ' }.toInt()
                    } catch (e: NumberFormatException) {
                        throw _root_ide_package_.com.gitee.wsl.doc.sample.opml.OpmlParseException("windowLeft must be a number")
                    }
                } else {
                    null
                }
            }

            "windowRight" -> {
                val trimmed = text.trim { it <= ' ' }
                windowRight = if (trimmed.isNotEmpty()) {
                    try {
                        text.trim { it <= ' ' }.toInt()
                    } catch (e: NumberFormatException) {
                        throw _root_ide_package_.com.gitee.wsl.doc.sample.opml.OpmlParseException("windowRight must be a number")
                    }
                } else {
                    null
                }
            }

            "windowTop" -> {
                val trimmed = text.trim { it <= ' ' }
                windowTop = if (trimmed.isNotEmpty()) {
                    try {
                        text.trim { it <= ' ' }.toInt()
                    } catch (e: NumberFormatException) {
                        throw _root_ide_package_.com.gitee.wsl.doc.sample.opml.OpmlParseException("windowTop must be a number")
                    }
                } else {
                    null
                }
            }
        }
    }

    @Throws(_root_ide_package_.com.gitee.wsl.doc.sample.opml.OpmlParseException::class)
    override fun endTag(xpp: XmlPullParser) {
        stack.pop()
        started = stack.isEmpty()
    }

    override fun get(): com.gitee.wsl.doc.sample.opml.bean.Head {
        return _root_ide_package_.com.gitee.wsl.doc.sample.opml.bean.Head(
            title,
            dateCreated,
            dateModified,
            ownerName,
            ownerEmail,
            ownerId,
            docs,
            expansionState,
            vertScrollState,
            windowTop,
            windowLeft,
            windowBottom,
            windowRight
        )
    }
}
